﻿using YunQue.Core.Common.Enums;

namespace YunQue.Core.Common.Helpers
{
    /// <summary>
    /// 处理时间的帮助类
    /// </summary>
    public class TimeHelper
    {
        /// <summary>
        /// 格式化时间范围
        /// </summary>
        /// <param name="startTime"></param>
        /// <param name="endTime"></param>
        /// <returns></returns>
        public static (string, string) FormatTimeRange(DateTime startTime, DateTime endTime)
        {
            var start = FormatTime(startTime);
            var end = FormatTime(endTime);
            return (start, end);
        }
        /// <summary>
        /// 格式化时间范围
        /// </summary>
        /// <param name="startTime"></param>
        /// <param name="endTime"></param>
        /// <param name="format"></param>
        /// <returns></returns>
        public static (string, string) FormatTimeRange(DateTime startTime, DateTime endTime, string format)
        {
            var start = FormatTime(startTime, format);
            var end = FormatTime(endTime, format);
            return (start, end);
        }
        /// <summary>
        /// 格式化时间
        /// </summary>
        /// <param name="dateTime"></param>
        /// <returns></returns>
        public static string FormatTime(DateTime dateTime)
        {
            return FormatTime(dateTime, "yyyy.MM.dd HH:mm:ss");
        }
        /// <summary>
        /// 格式化时间
        /// </summary>
        /// <param name="dateTime"></param>
        /// <param name="format"></param>
        /// <returns></returns>
        public static string FormatTime(DateTime dateTime, string format)
        {
            return dateTime.ToString(format);
        }
        /// <summary>
        /// 格式化时间
        /// </summary>
        /// <param name="dateTime"></param>
        /// <param name="timeSpan"></param>
        /// <param name="weekType"></param>
        /// <returns></returns>
        public static string FormatTime(DateTime dateTime, TimeSpanEnum timeSpan, WeekTypeEnum weekType = WeekTypeEnum.WeekOfYear)
        {
            var timeRange = GetTimeRange(dateTime, dateTime, timeSpan, weekType);
            return timeRange?.Keys?.FirstOrDefault();
        }
        /// <summary>
        /// 分割时间
        /// </summary>
        /// <param name="startTime"></param>
        /// <param name="endTime"></param>
        /// <param name="timeSpan"></param>
        /// <returns></returns>
        public static List<Tuple<DateTime, DateTime>> SplitTime(DateTime startTime, DateTime endTime, int timeSpan)
        {
            List<Tuple<DateTime, DateTime>> timeList = new List<Tuple<DateTime, DateTime>>();
            if (startTime >= endTime)
            {
                timeList.Add(new Tuple<DateTime, DateTime>(startTime, endTime));
                return timeList;
            }
            while (startTime < endTime)
            {
                DateTime tempEndTime = startTime.Date.AddDays(timeSpan).AddMilliseconds(-1);
                if (tempEndTime > endTime)
                {
                    tempEndTime = endTime;
                }
                timeList.Add(new Tuple<DateTime, DateTime>(startTime, tempEndTime));
                startTime = tempEndTime.AddMilliseconds(1);
            }
            return timeList;
        }
        /// <summary>
        /// 按时间范围获取日期
        /// </summary>
        /// <param name="startTime"></param>
        /// <param name="endTime"></param>
        /// <returns></returns>
        public static List<DateTime> GetDateRange(DateTime startTime, DateTime endTime)
        {
            List<DateTime> range = new List<DateTime>();
            startTime = startTime.Date;
            endTime = endTime.Date;
            if (startTime >= endTime)
            {
                range.Add(startTime);
                range.Add(endTime);
                return range;
            }
            while (startTime <= endTime)
            {
                range.Add(startTime);
                startTime = startTime.AddDays(1);
            }
            return range;
        }
        /// <summary>
        /// 分割时间
        /// </summary>
        /// <param name="startTime"></param>
        /// <param name="endTime"></param>
        /// <returns></returns>
        public static Dictionary<string, Tuple<DateTime, DateTime>> GetDateRangeDic(DateTime startTime, DateTime endTime)
        {
            //如果开始时间和结束时间是在同一个月，那么就分割为周
            //如果开始时间和结束时间是在同一年，那么就分割为月
            //如果开始时间和结束时间不在同一年，那么就分割为年(如果相差超过6个月，分割为年，不超过 分割为月)
            Dictionary<string, Tuple<DateTime, DateTime>> range = new Dictionary<string, Tuple<DateTime, DateTime>>();
            if (startTime.Year == endTime.Year)
            {
                if (startTime.Month == endTime.Month)
                {
                    //分割为周
                    range = GetWeekRange(startTime, endTime);
                }
                else
                {
                    //分割为月
                    range = GetMonthRange(startTime, endTime);
                }
            }
            else
            {
                //分割为年
                //如果相差超过6个月，分割为年，不超过 分割为月
                if (endTime.AddMonths(-6) > startTime)
                {
                    range = GetYearRange(startTime, endTime);
                }
                else
                {
                    range = GetMonthRange(startTime, endTime);
                }
            }
            return range;
        }
        /// <summary>
        /// 获取时间段范围
        /// </summary>
        /// <param name="startDate"></param>
        /// <param name="endDate"></param>
        /// <param name="timeSpan"></param>
        /// <param name="weekType"></param>
        /// <returns></returns>
        public static Dictionary<string, Tuple<DateTime, DateTime>> GetTimeRange(DateTime startDate, DateTime endDate, TimeSpanEnum timeSpan, WeekTypeEnum weekType = WeekTypeEnum.WeekOfYear)
        {
            switch (timeSpan)
            {
                case TimeSpanEnum.Day:
                    return GetDayRange(startDate, endDate);
                case TimeSpanEnum.Week:
                    return GetWeekRange(startDate, endDate, weekType);
                case TimeSpanEnum.Month:
                    return GetMonthRange(startDate, endDate);
                case TimeSpanEnum.Quarter:
                    return GetQuarterRange(startDate, endDate);
                case TimeSpanEnum.Year:
                    return GetYearRange(startDate, endDate);
                case TimeSpanEnum.HalfYear:
                    return GetHalfYearRange(startDate, endDate);
                default:
                    return GetDayRange(startDate, endDate);
            }
        }
        /// <summary>
        /// 按天分割时间
        /// </summary>
        /// <param name="startDate"></param>
        /// <param name="endDate"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static Dictionary<string, Tuple<DateTime, DateTime>> GetDayRange(DateTime startDate, DateTime endDate)
        {
            Dictionary<string, Tuple<DateTime, DateTime>> range = new Dictionary<string, Tuple<DateTime, DateTime>>();
            startDate = startDate.Date;
            endDate = endDate.Date;
            if (startDate > endDate) throw new Exception("开始时间不能大于结束时间");
            while (startDate <= endDate)
            {
                range.Add(startDate.ToString("yyyy-MM-dd"), new Tuple<DateTime, DateTime>(startDate, startDate.AddDays(1).AddMilliseconds(-1)));
                startDate = startDate.AddDays(1);
            }
            return range;
        }
        /// <summary>
        /// 按周分割时间
        /// </summary>
        /// <param name="startDate">开始日期</param>
        /// <param name="endDate">结束日期</param>
        /// <param name="weekType">周类型</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static Dictionary<string, Tuple<DateTime, DateTime>> GetWeekRange(DateTime startDate, DateTime endDate, WeekTypeEnum weekType = WeekTypeEnum.WeekOfMonth)
        {
            Dictionary<string, Tuple<DateTime, DateTime>> range = new Dictionary<string, Tuple<DateTime, DateTime>>();
            startDate = startDate.Date;
            endDate = endDate.Date;
            if (startDate > endDate) throw new Exception("开始时间不能大于结束时间");
            while (startDate <= endDate)
            {
                //获取startTime所在周的第几天
                int dayOfWeek = (int)startDate.DayOfWeek;
                if (dayOfWeek == 0) dayOfWeek = 7;
                //获取本周的结束时间
                DateTime dateTime = startDate.Date.AddDays(7 - dayOfWeek);
                //WeekOfMonth 如果结束时间是到下个月，那么就是本月最后一天
                if (weekType == WeekTypeEnum.WeekOfMonth && dateTime.Month != startDate.Month)
                {
                    dateTime = new DateTime(startDate.Year, startDate.Month, 1).AddMonths(1).AddDays(-1).Date;
                }
                DateTime tempEndTime = dateTime > endDate ? endDate : dateTime;
                //第几周
                int week = GetWeekIndex(startDate, weekType);
                range.Add(startDate.ToString($"{(weekType == WeekTypeEnum.WeekOfMonth ? "yyyy-MM" : "yyyy")}") + $" Week{week.ToString("")} ", new Tuple<DateTime, DateTime>(startDate, tempEndTime.AddDays(1).AddMilliseconds(-1)));
                startDate = tempEndTime.AddDays(1);
            }
            return range;
        }

        /// <summary>
        /// 按月分割时间
        /// </summary>
        /// <param name="startDate"></param>
        /// <param name="endDate"></param>
        /// <returns></returns>
        public static Dictionary<string, Tuple<DateTime, DateTime>> GetMonthRange(DateTime startDate, DateTime endDate)
        {
            Dictionary<string, Tuple<DateTime, DateTime>> range = new Dictionary<string, Tuple<DateTime, DateTime>>();
            startDate = startDate.Date;
            endDate = endDate.Date;
            if (startDate > endDate) throw new Exception("开始时间不能大于结束时间");
            while (startDate <= endDate)
            {
                //获取startTime所在月的最后一天
                DateTime tempEndTime = new DateTime(startDate.Year, startDate.Month, 1).AddMonths(1).AddMilliseconds(-1);
                if (tempEndTime > endDate)
                {
                    tempEndTime = endDate.AddDays(1).AddMilliseconds(-1);
                }
                range.Add(startDate.ToString("yyyy-MM"), new Tuple<DateTime, DateTime>(startDate, tempEndTime));
                startDate = tempEndTime.AddMilliseconds(1);
            }
            return range;
        }
        /// <summary>
        /// 按季度分割时间
        /// </summary>
        /// <param name="startDate"></param>
        /// <param name="endDate"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static Dictionary<string, Tuple<DateTime, DateTime>> GetQuarterRange(DateTime startDate, DateTime endDate)
        {
            Dictionary<string, Tuple<DateTime, DateTime>> range = new Dictionary<string, Tuple<DateTime, DateTime>>();
            startDate = startDate.Date;
            endDate = endDate.Date;
            if (startDate > endDate) throw new Exception("开始时间不能大于结束时间");
            while (startDate <= endDate)
            {
                //获取startTime所在季度的最后一天
                int quarter = GetQuarterByMonth(startDate.Month);
                DateTime tempEndTime = new DateTime(startDate.Year, quarter * 3, 1).AddMonths(1).AddMilliseconds(-1);
                if (tempEndTime > endDate)
                {
                    tempEndTime = endDate.AddDays(1).AddMilliseconds(-1);
                }
                range.Add($"{startDate.ToString("yyyy")} Q{quarter} ", new Tuple<DateTime, DateTime>(startDate, tempEndTime));
                startDate = tempEndTime.AddMilliseconds(1);
            }
            return range;
        }
        /// <summary>
        /// 按年分割时间
        /// </summary>
        /// <param name="startDate"></param>
        /// <param name="endDate"></param>
        /// <returns></returns>
        public static Dictionary<string, Tuple<DateTime, DateTime>> GetYearRange(DateTime startDate, DateTime endDate)
        {
            Dictionary<string, Tuple<DateTime, DateTime>> range = new Dictionary<string, Tuple<DateTime, DateTime>>();
            startDate = startDate.Date;
            endDate = endDate.Date;
            if (startDate > endDate) throw new Exception("开始时间不能大于结束时间");
            while (startDate <= endDate)
            {
                //获取startTime所在年的最后一天
                DateTime tempEndTime = new DateTime(startDate.Year, 12, 31).AddDays(1).AddMilliseconds(-1);
                if (tempEndTime > endDate)
                {
                    tempEndTime = endDate.AddDays(1).AddMilliseconds(-1);
                }
                range.Add(startDate.ToString("yyyy"), new Tuple<DateTime, DateTime>(startDate, tempEndTime));
                startDate = tempEndTime.AddMilliseconds(1);
            }
            return range;
        }
        /// <summary>
        /// 按照半年分割时间
        /// </summary>
        /// <param name="startDate"></param>
        /// <param name="endDate"></param>
        /// <returns></returns>
        public static Dictionary<string, Tuple<DateTime, DateTime>> GetHalfYearRange(DateTime startDate, DateTime endDate)
        {
            Dictionary<string, Tuple<DateTime, DateTime>> range = new Dictionary<string, Tuple<DateTime, DateTime>>();
            startDate = startDate.Date;
            endDate = endDate.Date;
            if (startDate > endDate) throw new Exception("开始时间不能大于结束时间");
            while (startDate <= endDate)
            {
                //获取startTime所在半年的最后一天
                int halfYear = startDate.Month > 6 ? 2 : 1;
                DateTime tempEndTime = new DateTime(startDate.Year, halfYear * 6, 1).AddMonths(1).AddMilliseconds(-1);
                if (tempEndTime > endDate)
                {
                    tempEndTime = endDate.AddDays(1).AddMilliseconds(-1);
                }
                range.Add($"{startDate.ToString("yyyy")} H{halfYear} ", new Tuple<DateTime, DateTime>(startDate, tempEndTime));
                startDate = tempEndTime.AddMilliseconds(1);
            }
            return range;
        }

        /// <summary>
        /// 获取日期是第几周
        /// </summary>
        /// <param name="date">日期</param>
        /// <param name="weekType">周类型</param>
        /// <returns></returns>
        public static int GetWeekIndex(DateTime date, WeekTypeEnum weekType = WeekTypeEnum.WeekOfMonth)
        {
            DateTime firstDay = new DateTime(date.Year, weekType == WeekTypeEnum.WeekOfMonth ? date.Month : 1, 1);
            int firstWeekday = (int)firstDay.DayOfWeek;
            if (firstWeekday == 0) firstWeekday = 7;
            DateTime firstWeekEndDate = firstDay.AddDays(7 - firstWeekday);
            if (date <= firstWeekEndDate) return 1;
            int week = (date.DayOfYear - firstWeekEndDate.DayOfYear) / 7;
            int mpValue = (date.DayOfYear - firstWeekEndDate.DayOfYear) % 7;
            return mpValue == 0 ? week + 1 : week + 2;
        }
        /// <summary>
        /// 获取月份是第几季度
        /// </summary>
        /// <param name="month"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static int GetQuarterByMonth(int month)
        {
            if (month < 1 || month > 12) throw new Exception("月份不正确");
            return (month - 1) / 3 + 1;
        }

        /// <summary>
        /// 获取相同时间间隔类型的前一个时间范围
        /// </summary>
        /// <param name="date"></param>
        /// <param name="timeSpan"></param>
        /// <param name="weekType"></param>
        /// <returns></returns>
        public static Tuple<DateTime, DateTime> GetPreTimeRange(DateTime date, TimeSpanEnum timeSpan = TimeSpanEnum.Day, WeekTypeEnum weekType = WeekTypeEnum.WeekOfYear)
        {
            DateTime startTime = date.Date;
            DateTime endTime = date.AddDays(1).Date.AddMicroseconds(-1);
            switch (timeSpan)
            {
                case TimeSpanEnum.Day:
                    startTime = startTime.AddDays(-1).Date;
                    endTime = startTime.AddDays(1).Date.AddMilliseconds(-1);
                    break;
                case TimeSpanEnum.Week:
                    var weekRange = GetWeekRange(startTime.AddDays(-14), startTime, weekType);
                    //倒数第二个key
                    var key = weekRange.Keys.ElementAt(weekRange.Count - 2);
                    startTime = weekRange[key].Item1;
                    endTime = weekRange[key].Item2;
                    break;
                case TimeSpanEnum.Month:
                    startTime = new DateTime(startTime.Year, startTime.Month, 1).AddMonths(-1);
                    endTime = startTime.AddMonths(1).Date.AddMilliseconds(-1);
                    break;
                case TimeSpanEnum.Quarter:
                    int quarter = GetQuarterByMonth(startTime.Month);
                    if (quarter == 1)
                    {
                        startTime = new DateTime(startTime.Year - 1, 10, 1);
                    }
                    else
                    {
                        startTime = new DateTime(startTime.Year, (quarter - 2) * 3 + 1, 1);
                    }
                    endTime = startTime.AddMonths(3).AddMilliseconds(-1);
                    break;
                case TimeSpanEnum.Year:
                    startTime = new DateTime(startTime.Year - 1, 1, 1);
                    endTime = new DateTime(startTime.Year, 12, 31).AddDays(1).AddMilliseconds(-1);
                    break;

                default:
                    break;
            }
            return new Tuple<DateTime, DateTime>(startTime, endTime);
        }
        /// <summary>
        /// 获取相同时间间隔类型的前一个时间范围
        /// </summary>
        /// <param name="date"></param>
        /// <param name="timeSpan"></param>
        /// <param name="weekType"></param>
        /// <returns></returns>
        public static Tuple<DateTime, DateTime> GetNextTimeRange(DateTime date, TimeSpanEnum timeSpan = TimeSpanEnum.Day, WeekTypeEnum weekType = WeekTypeEnum.WeekOfYear)
        {
            DateTime startTime = date.Date;
            DateTime endTime = date.AddDays(1).Date.AddMicroseconds(-1);
            switch (timeSpan)
            {
                case TimeSpanEnum.Day:
                    startTime = startTime.AddDays(1).Date;
                    endTime = startTime.AddDays(1).Date.AddMilliseconds(-1);
                    break;
                case TimeSpanEnum.Week:
                    var weekRange = GetWeekRange(startTime, startTime.AddDays(14), weekType);
                    //第二个key
                    var key = weekRange.Keys.ElementAt(1);
                    startTime = weekRange[key].Item1;
                    endTime = weekRange[key].Item2;
                    break;
                case TimeSpanEnum.Month:
                    startTime = new DateTime(startTime.Year, startTime.Month, 1).AddMonths(1);
                    endTime = startTime.AddMonths(1).Date.AddMilliseconds(-1);
                    break;
                case TimeSpanEnum.Quarter:
                    int quarter = GetQuarterByMonth(startTime.Month);
                    if (quarter == 4)
                    {
                        startTime = new DateTime(startTime.Year + 1, 1, 1);
                    }
                    else
                    {
                        startTime = new DateTime(startTime.Year, (quarter) * 3 + 1, 1);
                    }
                    endTime = startTime.AddMonths(3).AddMilliseconds(-1);
                    break;
                case TimeSpanEnum.Year:
                    startTime = new DateTime(startTime.Year + 1, 1, 1);
                    endTime = new DateTime(startTime.Year, 12, 31).AddDays(1).AddMilliseconds(-1);
                    break;

                default:
                    break;
            }
            return new Tuple<DateTime, DateTime>(startTime, endTime);
        }

        /// <summary>
        /// 按照不同的时间粒度获取时间函数
        /// </summary>
        /// <param name="timeSpan"></param>
        /// <param name="filterDate"></param>
        /// <returns></returns>
        public static string GetTimeFunc(TimeSpanEnum timeSpan, string filterDate)
        {
            string timeFunc = string.Empty;
            switch (timeSpan)
            {
                case TimeSpanEnum.Day:
                    timeFunc = $"DATE_TRUNC('day', {filterDate}) AS datadate";
                    break;
                case TimeSpanEnum.Week:
                    timeFunc = $"DATE_TRUNC('week', {filterDate}) AS datadate";
                    break;
                case TimeSpanEnum.Month:
                    timeFunc = $"DATE_TRUNC('month', {filterDate}) AS datadate";
                    break;
                case TimeSpanEnum.Quarter:
                    timeFunc = $"DATE_TRUNC('quarter', {filterDate}) AS datadate";
                    break;
                case TimeSpanEnum.Year:
                    timeFunc = $"DATE_TRUNC('year', {filterDate}) AS datadate";
                    break;
            }
            return timeFunc;
        }
    }
}
